//
//  YahooStockExchangeHtmlInterpreter.m
//  MarketDataColloctor
//
//  Created by jiro on 10/03/09.
//  Copyright 2010 jiro music. All rights reserved.
//

#import "YahooStockExchangeHtmlInterpreter.h"
#import "../../../DbTypes/DbTypes.h"

const NSString* const kText_HtmlInterpretError = @"html interpret error";

@implementation YahooStockExchangeHtmlInterpreter

- (id) interpret: (NSString*) html
{
	StockExchangeJaList* list = [ [ StockExchangeJaList alloc ] init ];
	
	// ---- 前提条件チェック ---- //
	if ( [ html isEqualToString: @"" ] ||
		 [ html rangeOfString: @"の価格データはありません" ].location != NSNotFound ||
		 [ html rangeOfString: @"この検索期間の価格データはありません。" ].location != NSNotFound )
	{
		return list;
	}
	// ----------------------- //

	NSError* error = nil;
	NSXMLDocument* document = [ [ NSXMLDocument alloc ] initWithXMLString: html options: NSXMLDocumentTidyHTML error: & error ];
	if ( error == nil )
	{
		errorType = HtmlInterpretError_ReadFile;
		return nil;
	}

	NSXMLElement* element = [ document rootElement ];
	NSXMLNode* node;

	NSString* brandCode;
	NSString* exchangeDate;
	NSString* openPrice;
	NSString* closePrice;
	NSString* highPrice;
	NSString* lowPrice;
	NSString* volume;
	NSString* adjustRate;
	@try {

		//	---	銘柄コードのある所までスキップ
		node = [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ element childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 0 ] childAtIndex: 13 ] 
					childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 0 ] childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 1 ] 
					childAtIndex: 0 ] childAtIndex: 0 ] childAtIndex: 1 ] childAtIndex: 2 ] childAtIndex: 4 ];
		node = [ (NSXMLElement*) node attributeForName: @"value" ];
		if ( node == nil )
		{
			errorType = HtmlInterpretError_NotFoundBrandCode;
			return nil;
		}
//		NSLog( @"%@", [ node stringValue ] );
		brandCode = [ node stringValue ];
		NSRange range = [ brandCode rangeOfString: @"." ];
		if ( range.location != NSNotFound )
		{
			brandCode = [ brandCode substringToIndex: range.location ];
		}
		//	---

		//	---	株のデータのある所までスキップ
		node = [ [ [ [ [ [ [ [ [ [ [ element childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 0 ] childAtIndex: 15 ] childAtIndex: 0 ]
					childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 3 ] childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 0 ];
		//	---

		//	---	正しいノードを取得しているかチェック
		NSArray* a = [ node children ];
		NSEnumerator* e = [ a objectEnumerator ];
		[ e nextObject ];	//	不要なノードをスキップ
		node = [ e nextObject ];
		if ( [ node childCount ] != 8 )
		{
			@throw [ NSException exceptionWithName: kText_HtmlInterpretError reason: @"no headers of talbe" userInfo: nil ];
//			errorType = HtmlInterpretError_NotFoundHeadOfTable;
//			return NO;
		}
		if ( NO == [ [ [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] childAtIndex: 0 ] stringValue ] isEqualToString: @"日付" ] )
		{
			errorType = HtmlInterpretError_InvalidHeadOfTable;
			errorMessage = @"'項目'が見つかりません";
			return NO;
		}
		if ( NO == [ [ [ [ [ node childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 0 ] stringValue ] isEqualToString: @"始値" ] )
		{
			errorType = HtmlInterpretError_InvalidHeadOfTable;
			errorMessage = @"'始値'が見つかりません";
			return NO;
		}
		if ( NO == [ [ [ [ [ node childAtIndex: 2 ] childAtIndex: 0 ] childAtIndex: 0 ] stringValue ] isEqualToString: @"高値" ] )
		{
			errorType = HtmlInterpretError_InvalidHeadOfTable;
			errorMessage = @"'高値'が見つかりません";
			return NO;
		}
		if ( NO == [ [ [ [ [ node childAtIndex: 3 ] childAtIndex: 0 ] childAtIndex: 0 ] stringValue ] isEqualToString: @"安値" ] )
		{
			errorType = HtmlInterpretError_InvalidHeadOfTable;
			errorMessage = @"'安値'が見つかりません";
			return NO;
		}
		if ( NO == [ [ [ [ [ node childAtIndex: 4 ] childAtIndex: 0 ] childAtIndex: 0 ] stringValue ] isEqualToString: @"終値" ] )
		{
			errorType = HtmlInterpretError_InvalidHeadOfTable;
			errorMessage = @"'終値'が見つかりません";
			return NO;
		}
		if ( NO == [ [ [ [ [ node childAtIndex: 5 ] childAtIndex: 0 ] childAtIndex: 0 ] stringValue ] isEqualToString: @"出来高" ] )
		{
			errorType = HtmlInterpretError_InvalidHeadOfTable;
			errorMessage = @"'出来高'が見つかりません";
			return NO;
		}
		if ( NO == [ [ [ [ [ node childAtIndex: 6 ] childAtIndex: 0 ] childAtIndex: 0 ] stringValue ] isEqualToString: @"調整後終値*" ] )
		{
			errorType = HtmlInterpretError_InvalidHeadOfTable;
			errorMessage = @"'調整後終値*'が見つかりません";
			return NO;
		}
		//	---

		NSString* value;
		while ( ( node = [ e nextObject ] ) != nil )
		{
			int childCount = [ node childCount ];
			
			node = [ node childAtIndex: 0 ];

			//	日付
			NSRange range;
			value = [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];

			//	--- エラーチェック
			range = [ value rangeOfString: @"年" ];
			if ( range.location == NSNotFound )
			{
				errorType = HtmlInterpretError_NotFoundDate;
				errorMessage = [ NSString stringWithFormat: @"\n%@", [ node XMLStringWithOptions: NSXMLNodePrettyPrint ] ];
				return NO;
			}
			range = [ value rangeOfString: @"月" ];
			if ( range.location == NSNotFound )
			{
				errorType = HtmlInterpretError_NotFoundDate;
				errorMessage = [ NSString stringWithFormat: @"\n%@", [ node XMLStringWithOptions: NSXMLNodePrettyPrint ] ];
				return NO;
			}
			range = [ value rangeOfString: @"日" ];
			if ( range.location == NSNotFound )
			{
				errorType = HtmlInterpretError_NotFoundDate;
				errorMessage = [ NSString stringWithFormat: @"\n%@", [ node XMLStringWithOptions: NSXMLNodePrettyPrint ] ];
				return NO;
			}
			//	---

			value = [ value stringByReplacingOccurrencesOfString: @"年" withString: @"-" ];
			value = [ value stringByReplacingOccurrencesOfString: @"月" withString: @"-" ];
			value = [ value stringByReplacingOccurrencesOfString: @"日" withString: @"" ];
			exchangeDate = [ NSString stringWithFormat: @"%@ 00:00:00 +0900", value ];

			if ( childCount == 8 )
			{	//	株取引のデータ
				node = [ node nextSibling ];
				value = [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];
				if ( [ value isEqualToString: @"---" ] ) {
					value = @"null";
				}
				openPrice = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];

				node = [ node nextSibling ];
				value = [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];
				if ( [ value isEqualToString: @"---" ] ) {
					value = @"null";
				}
				highPrice = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];

				node = [ node nextSibling ];
				value = [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];
				if ( [ value isEqualToString: @"---" ] ) {
					value = @"null";
				} 
				lowPrice = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];

				node = [ node nextSibling ];
				value = [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];
				if ( [ value isEqualToString: @"---" ] ) {
					value = @"null";
				} 
				closePrice = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];

				node = [ node nextSibling ];
				value = [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];
				if ( [ value isEqualToString: @"---" ] ) {
					value = @"null";
				} 
				volume = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];

				node = [ node nextSibling ];
				value = [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];
				value = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];
				float fEndPrice = [ closePrice intValue ];
				float fAdjustedEndPrice = [ value intValue ];
				adjustRate = [ NSString stringWithFormat: @"%.4f", fAdjustedEndPrice / fEndPrice ];

				StockExchangeJa* stockExchangeJa	= [ [ StockExchangeJa alloc ] init ];
				stockExchangeJa.code				= brandCode;
				stockExchangeJa.exchangeDate		= [ NSDate dateWithNaturalLanguageString: exchangeDate ];
				stockExchangeJa.openPrice			= [ openPrice floatValue ];
				stockExchangeJa.closePrice			= [ closePrice floatValue ];
				stockExchangeJa.highPrice			= [ highPrice floatValue ];
				stockExchangeJa.lowPrice			= [ lowPrice floatValue ];
				stockExchangeJa.volume				= [ volume floatValue ];
				stockExchangeJa.adjustedEndPrice	= fAdjustedEndPrice;

				[ list addObject: stockExchangeJa ];

			}
			else
			{	//	分割データ
				node = [ node nextSibling ];
				value = [ [ [ node childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];
				NSRange range = [ value rangeOfString: @"分割" ];
				if ( range.location == NSNotFound ) {
					errorType = HtmlInterpretError_UnknownHtmlNode;
					errorMessage = [ NSString stringWithFormat: @"\n%@", [ node XMLStringWithOptions: NSXMLNodePrettyPrint ] ];
					return NO;
				}

				range = [ value rangeOfString: @" -> " ];
				if ( range.location == NSNotFound )
				{
					errorType = HtmlInterpretError_UnknownHtmlNode;
					errorMessage = [ NSString stringWithFormat: @"\n%@", [ node XMLStringWithOptions: NSXMLNodePrettyPrint ] ];
					return NO;
				}
				value = [ value stringByReplacingCharactersInRange: NSMakeRange( 0, range.location + range.length ) withString: @"" ];
				value = [ value stringByReplacingOccurrencesOfString: @"分割: " withString: @"" ];
				value = [ value stringByReplacingOccurrencesOfString: @"株" withString: @"" ];

				StockUnitDivitionJa* division = [ [ StockUnitDivitionJa alloc ] init ];
				division.code = brandCode;
				division.divisionDate = [ NSDate dateWithNaturalLanguageString: exchangeDate ];
				division.unitDivided = [ value floatValue ];
				
				[ list addObject: division ];
			}
		}
	}
	@catch ( NSException* exception )
	{
//		NSLog( @"%@", [ exception name ] );
		if ( NO == [ [ exception name ] isEqualToString: kText_HtmlInterpretError ] )
		{
			NSLog( @"HTMLの解析に失敗　銘柄コード: %@", brandCode );
			errorType = HtmlInterpretError_NotFoundHeadOfTable;
			return nil;
		}
		
		if ( YES == [ brandCode isEqualToString: @"998407" ] ||		//	nikkei average
			 YES == [ brandCode isEqualToString: @"998405" ] )		//	topix
		{
			node = [ [ [ [ [ [ [ [ [ [ [ [ document rootElement ] childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 0 ] 
				childAtIndex: 15 ] childAtIndex: 0 ] childAtIndex: 1 ] childAtIndex: 0 ] childAtIndex: 3 ] childAtIndex: 1 ]
				childAtIndex: 0 ] childAtIndex: 0 ];

//			NSLog( @"child count: %d", [ node childCount ] );
//			for ( int i = 0; i < [ node childCount ]; i++ )
//			{
//				NSLog( @"i = %d: \n%@", i, [ [ node childAtIndex: i ] description ] );
//			}

			NSArray* children = [ node children ];
			NSEnumerator* e = [ children objectEnumerator ];
			NSXMLNode* exchangeNode;
			NSString* value;
			[ e nextObject ];	//	skip unnecessary node
			[ e nextObject ];	//	skip unnecessary node
			while ( exchangeNode = [ e nextObject ] )
			{
//				NSLog( @"child count: %d", [ exchangeNode childCount ] );
//				for ( int i = 0; i < [ exchangeNode childCount ]; i++ )
//				{
//					NSLog( @"i = %d: \n%@", i, [ [ exchangeNode childAtIndex: i ] description ] );
//				}
//				NSLog( @"銘柄コード: %@", brandCode );
				
				StockExchangeJa* exchange = [ [ StockExchangeJa alloc ] init ];

				exchange.code = brandCode;

				//	exchange date
				value = [ [ [ exchangeNode childAtIndex: 0 ] childAtIndex: 0 ] stringValue ];
				value = [ value stringByReplacingOccurrencesOfString: @"年" withString: @"-" ];
				value = [ value stringByReplacingOccurrencesOfString: @"月" withString: @"-" ];
				value = [ value stringByReplacingOccurrencesOfString: @"日" withString: @"" ];
				exchange.exchangeDate = [ NSDate dateWithString: [ NSString stringWithFormat: @"%@ 00:00:00 +0900", value ] ];

				//	start price
				value = [ [ [ exchangeNode childAtIndex: 1 ] childAtIndex: 0 ] stringValue ];
				value = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];
				exchange.openPrice = [ value floatValue ]; 

				//	heighest price
				value = [ [ [ exchangeNode childAtIndex: 2 ] childAtIndex: 0 ] stringValue ];
				value = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];
				exchange.highPrice = [ value floatValue ]; 
				
				//	lowest price
				value = [ [ [ exchangeNode childAtIndex: 3 ] childAtIndex: 0 ] stringValue ];
				value = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];
				exchange.lowPrice = [ value floatValue ]; 

				//	end price
				value = [ [ [ exchangeNode childAtIndex: 4 ] childAtIndex: 0 ] stringValue ];
				value = [ value stringByReplacingOccurrencesOfString: @"," withString: @"" ];
				exchange.closePrice = [ value floatValue ]; 

				exchange.adjustedEndPrice = [ value floatValue ];

				[ list addObject: exchange ];
			}
		}		
	}	
	[ document release ];

	return list;
}

@end
